﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SocialLotus.Entities;
using NHibernate;
using System.Data;
using System.Data.SqlClient;
using NHibernate.Criterion;
using NHibernate.Linq;
using System.Linq.Expressions;

namespace SocialLotus.DAO
{
    public class GenericDAO<T> : BaseDAO where T : PersistedEntity
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="GenericDAO&lt;T&gt;"/> class.
        /// </summary>
        /// <param name="session">The NHibernate session.</param>
        internal GenericDAO(ISession session) : base(session) {  }

        /// <summary>
        /// Saves the given entity (Insert).
        /// </summary>
        /// <param name="entity">The entity.</param>
        /// <returns>The saved entity</returns>
        public T Save(T entity)
        {
            Session.Save(entity);

            return entity;
        }

        /// <summary>
        /// Saves the given entity (Insert or Update).
        /// </summary>
        /// <param name="entity">The entity.</param>
        /// <param name="FlushChanges">if set to <c>true</c> [flush changes]</param>
        /// <returns>The saved entity.</returns>
        public T SaveOrUpdate(T entity, bool FlushChanges = true)
        {
            //Session.SaveOrUpdate(entity);
            Session.SaveOrUpdate(entity);
            if (FlushChanges)
                Session.Flush();
            return entity;
        }


        /// <summary>
        /// Deletes the given entity.
        /// </summary>
        /// <param name="entity">The entity.</param>
        public void Delete(T entity, bool FlushChanges = true)
        {
            Session.Delete(entity);
            if (FlushChanges)
                Session.Flush();
        }

        /// <summary>
        /// Gets the T with the given ID.
        /// </summary>
        /// <param name="id">The ID.</param>
        /// <returns></returns>
        public T GetById(object id)
        {
            return Session.Get<T>(id);
        }
        
        /// <summary>
        /// Gets a list of all Ts.
        /// </summary>
        /// <param name="predicate">The predicate.</param>
        /// <returns></returns>
        public int GetMaxValue()
        {
            ICriteria criteria = Session.CreateCriteria<T>()
                .SetProjection(Projections.Max("ID"));

            return Convert.ToInt32(criteria.List()[0]);

            //return Session.Linq<T>()
            //     .ToList<T>();

        }

        /// <summary>
        /// Gets a list of all Ts.
        /// </summary>
        /// <param name="predicate">The predicate.</param>
        /// <returns></returns>
        public int GetCountOfRecords()
        {
            return Session.Linq<T>()
                .Count();
        }

        /// <summary>
        /// Gets a list of all Ts.
        /// </summary>
        /// <param name="predicate">The predicate.</param>
        /// <returns></returns>
        public int GetCountOfRecords(Expression<Func<T, bool>> predicate)
        {
            return Session.Linq<T>()
                .Where(predicate)
                .Count();
        }
        
        public IList<T> GetList()
        {
            return Session.Linq<T>()
                .ToList<T>();
        }

        /// <summary>
        /// Gets a list of Ts matching the given predicate.
        /// </summary>
        /// <param name="predicate">The predicate.</param>
        /// <returns>The list of Ts.</returns>
        public IList<T> GetList(Expression<Func<T, bool>> predicate)
        {
            return Session.Linq<T>()
                .Where(predicate)
                //.Select(o => o)
                .ToList<T>();
        }

        public IList<T> GetDistinctList(Expression<Func<T, bool>> predicate)
        {
            return Session.Linq<T>()
                .Where(predicate)
                .Distinct()
                .ToList<T>();
        }

        public IList<T> GetLimitedList(int count)
        {
            return Session.Linq<T>()
                .Select(o => o)
                .Take(count)
                .ToList<T>();
        }

        public IList<T> GetLimitedList(Expression<Func<T, bool>> predicate, int count)
        {
            return Session.Linq<T>()
                .Select(o => o)
                .Take(count)
                .Where(predicate)
                .ToList<T>();
        }

        /// <summary>
        /// Gets the criteria result with help of a list of complex criterion-statements. Returns a list of given object T.
        /// </summary>
        /// <param name="expressions">The expressions with complex ICriterion-objects.</param>
        /// <returns></returns>
        public IList<T> GetCriteriaResult(List<ICriterion> expressions)
        {
            ICriteria criteria = Session.CreateCriteria(typeof(T));
            foreach (ICriterion expression in expressions)
            {
                criteria.Add(expression);
            }

            return criteria.List<T>();
        }
        
        /// <summary>
        /// Gets a single T matching the predicate.
        /// </summary>
        /// <param name="predicate">The predicate.</param>
        /// <returns>The matching T. If zero or multiple Ts match the predicate, null is returned.</returns>
        public T GetEntity(Expression<Func<T, bool>> predicate)
        {
            T entity = null;

            try
            {
                entity = Session.Linq<T>()
                    .Where(predicate)
                    .SingleOrDefault();
            }
            catch (InvalidOperationException)
            {
            }

            return entity;
        }

        /// <summary>
        /// Gets a single T matching the predicate.
        /// </summary>
        /// <param name="predicates">The predicates.</param>
        /// <returns>The matching T. If zero or multiple Ts match the predicate, null is returned.</returns>
        public T GetEntity(IList<Expression<Func<T, bool>>> predicates)
        {
            Expression<Func<T, bool>> expression = CombineExpressions(predicates);
            return GetEntity(expression);
        }

        /// <summary>
        /// Combines the expressions using the 'AND operator'.
        /// </summary>
        /// <param name="predicates">The predicates.</param>
        /// <returns></returns>
        private Expression<Func<T, bool>> CombineExpressions(IList<Expression<Func<T, bool>>> predicates)
        {
            Expression<Func<T, bool>> expression = o => true;

            foreach (Expression<Func<T, bool>> p in predicates)
            {
                expression = expression.And(p);
            }

            return expression;
        }

        /// <summary>
        /// Determines whether the specified predicate has a Record.
        /// </summary>
        /// <param name="predicate">The predicate.</param>
        /// <returns>
        /// 	<c>true</c> if the specified predicate has a Record; otherwise, <c>false</c>.
        /// </returns>
        public bool HasRecord(Expression<Func<T, bool>> predicate)
        {
            return Session.Linq<T>().Where(predicate).Count() > 0;
        }

        public IList<T> GetEntitiyListByIdArray(int[] idArray)
        {
            ICriteria criteria = Session.CreateCriteria<T>();
            criteria.Add(NHibernate.Criterion.Expression.In("ID", idArray));
            return criteria.List<T>();


        }
    }
}
